package cfca.seal.util;

import cfca.sadk.algorithm.common.Mechanism;
import cfca.sadk.algorithm.common.PKCSObjectIdentifiers;
import cfca.sadk.algorithm.common.PKIException;
import cfca.sadk.algorithm.sm2.SM2PublicKey;
import cfca.sadk.algorithm.util.SM2OIDUtil;
import cfca.sadk.asn1.parser.ASN1Parser;
import cfca.sadk.lib.crypto.Session;
import cfca.sadk.org.bouncycastle.asn1.ASN1EncodableVector;
import cfca.sadk.org.bouncycastle.asn1.ASN1Integer;
import cfca.sadk.org.bouncycastle.asn1.ASN1ObjectIdentifier;
import cfca.sadk.org.bouncycastle.asn1.ASN1OctetString;
import cfca.sadk.org.bouncycastle.asn1.ASN1Sequence;
import cfca.sadk.org.bouncycastle.asn1.ASN1Set;
import cfca.sadk.org.bouncycastle.asn1.DERBitString;
import cfca.sadk.org.bouncycastle.asn1.DERNull;
import cfca.sadk.org.bouncycastle.asn1.DEROctetString;
import cfca.sadk.org.bouncycastle.asn1.DERPrintableString;
import cfca.sadk.org.bouncycastle.asn1.DERSequence;
import cfca.sadk.org.bouncycastle.asn1.DERSet;
import cfca.sadk.org.bouncycastle.asn1.pkcs.CertificationRequest;
import cfca.sadk.org.bouncycastle.asn1.pkcs.CertificationRequestInfo;
import cfca.sadk.org.bouncycastle.asn1.pkcs.RSAPublicKey;
import cfca.sadk.org.bouncycastle.asn1.x500.X500Name;
import cfca.sadk.org.bouncycastle.asn1.x509.AlgorithmIdentifier;
import cfca.sadk.org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
import cfca.sadk.org.bouncycastle.crypto.params.RSAKeyParameters;
import cfca.sadk.org.bouncycastle.crypto.util.PublicKeyFactory;
import cfca.sadk.org.bouncycastle.jcajce.provider.asymmetric.rsa.BCRSAPublicKey;
import cfca.sadk.org.bouncycastle.pkcs.PKCS10CertificationRequest;
import cfca.sadk.system.global.P10RequestContextConfig;
import cfca.sadk.util.Base64;
import cfca.sadk.util.KeyUtil;
import cfca.sadk.util.Signature;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.math.BigInteger;
import java.security.KeyPair;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.util.Map;

public class PKCS10
{
  private String subject = null;
  private PublicKey publicKey = null;
  private PublicKey tempPublicKey = null;
  private ASN1Set attributes = null;
  private KeyPair keyPair = null;
  private KeyPair tempKeyPair;
  private Session session = null;
  private boolean needVerify = false;
  private boolean p10RequestVerifyState = false;

  private int certReqType = 0;

  ASN1ObjectIdentifier signAlgOID = null;
  String signAlg = null;
  Mechanism mechanism = null;
  byte[] signature = null;
  byte[] source = null;

  public PKCS10() {
  }

  public PKCS10(Session session) {
    this.session = session;
  }

  public void load(byte[] data)
    throws PKIException
  {
    BufferedReader bufferReader = new BufferedReader(new InputStreamReader(new ByteArrayInputStream(data)));
    try {
      String dataFromReadLine = bufferReader.readLine();
      if ((StringUtil.isNotEmpty(dataFromReadLine)) && (dataFromReadLine.toUpperCase().indexOf("BEGIN NEW CERTIFICATE REQUEST") != -1)) {
        StringBuffer strBuffer = new StringBuffer();
        while ((null != (dataFromReadLine = bufferReader.readLine())) && 
          (dataFromReadLine.toUpperCase().indexOf("END NEW CERTIFICATE REQUEST") == -1))
        {
          strBuffer.append(dataFromReadLine);
        }
        data = strBuffer.toString().getBytes();
      }

      if (ASN1Parser.isBase64Compatability(data))
        data = Base64.decode(data);
    }
    catch (IOException ex1)
    {
      throw new PKIException(PKIException.PARSE_P10_ERR, PKIException.PARSE_P10_ERR_DES, ex1);
    }

    CertificationRequest certificationRequest = null;
    try {
      if (data[0] != 48) {
        throw new Exception();
      }

      ASN1Sequence sequence = (ASN1Sequence)ASN1Parser.parseBytes2DERObj(data);
      certificationRequest = new CertificationRequest(sequence);
    } catch (Exception ex) {
      throw new PKIException(PKIException.PARSE_P10_ERR, PKIException.PARSE_P10_ERR_DES, new Exception("The PKCS10 CertificationRequest content error."));
    }
    load(certificationRequest);
  }

  public void load(CertificationRequest certficationRequest)
    throws PKIException
  {
    this.signAlgOID = certficationRequest.getSignatureAlgorithm().getAlgorithm();
    this.signAlg = ((String)Mechanism.OIDALGMap.get(this.signAlgOID));
    this.mechanism = new Mechanism(this.signAlg);
    if ((!"sha1WithRSAEncryption".equals(this.signAlg)) && (!"sha256WithRSAEncryption".equals(this.signAlg)) && (!"sha512WithRSAEncryption".equals(this.signAlg)) && (!"sm3WithSM2Encryption".equals(this.signAlg)) && (!"md5WithRSAEncryption".equals(this.signAlg)) && (!"md2WithRSAEncryption".equals(this.signAlg)))
    {
      throw new PKIException(PKIException.GEN_P10_ERR, PKIException.GEN_P10_ERR_DES + " " + PKIException.NONSUPPORT_SIGALG_DES + " " + this.signAlg);
    }

    this.signature = certficationRequest.getSignature().getBytes();
    if ("sm3WithSM2Encryption".equals(this.signAlg)) {
      try {
        this.signature = prepareSignature(this.signature, false);
      } catch (IOException e) {
        throw new PKIException(e.getMessage());
      }
    }
    CertificationRequestInfo crqInfo = certficationRequest.getCertificationRequestInfo();

    this.source = ASN1Parser.parseDERObj2Bytes(crqInfo.toASN1Primitive());
    SubjectPublicKeyInfo spkInfo = crqInfo.getSubjectPublicKeyInfo();

    this.needVerify = P10RequestContextConfig.getP10RequestVerifyState();
    this.subject = crqInfo.getSubject().toString();
    this.attributes = crqInfo.getAttributes();
    if ((this.attributes != null) && (this.attributes.size() > 1))
    {
      prepareTemporaryPublicKey();
    }
    try
    {
      if ("sm3WithSM2Encryption".equals(this.signAlg)) {
        this.publicKey = new SM2PublicKey(spkInfo.getPublicKeyData().getBytes());
      }
      else
      {
        RSAKeyParameters param = null;
        param = (RSAKeyParameters)PublicKeyFactory.createKey(spkInfo);
        this.publicKey = new BCRSAPublicKey(param);
      }
    } catch (IOException e) {
      throw new PKIException(e.getMessage());
    }

    if (this.needVerify) {
      this.p10RequestVerifyState = this.session.verify(this.mechanism, this.publicKey, this.source, this.signature);
      if (!this.p10RequestVerifyState)
        throw new PKIException(PKIException.PARSE_P10_ERR, PKIException.PARSE_P10_ERR_DES + " " + PKIException.PARSE_P10_ERR_VERIFY_SIG_DES);
    }
  }

  public int getKeySize()
  {
    if ("sm3WithSM2Encryption".equals(this.mechanism.getMechanismType())) {
      return 256;
    }
    BCRSAPublicKey bcPublicKey = (BCRSAPublicKey)this.publicKey;
    BigInteger n = bcPublicKey.getModulus();
    return n.bitLength();
  }

  public ASN1Set getAttributes()
  {
    return this.attributes;
  }

  public PublicKey getPublicKey()
  {
    return this.publicKey;
  }

  public String getSubject()
  {
    return this.subject;
  }

  private void prepareTemporaryPublicKey()
    throws PKIException
  {
    ASN1Sequence sequence = (ASN1Sequence)this.attributes.getObjectAt(1);
    ASN1ObjectIdentifier oid = ASN1ObjectIdentifier.getInstance(sequence.getObjectAt(0));

    if (!oid.equals(PKCSObjectIdentifiers.pkcs_9_at_tempPublicKey)) {
      return;
    }

    this.certReqType = 1;
    ASN1OctetString asn1oct = ASN1OctetString.getInstance(sequence.getObjectAt(1));
    sequence = ASN1Parser.parseOCT2SEQ(asn1oct);
    byte[] data = ((ASN1OctetString)sequence.getObjectAt(1)).getOctets();

    if (!"sm3WithSM2Encryption".equals(this.signAlg))
    {
      RSAPublicKey rsaPubKey = RSAPublicKey.getInstance(data);
      RSAKeyParameters rsaKeyParams = new RSAKeyParameters(false, rsaPubKey.getModulus(), rsaPubKey.getPublicExponent());
      this.tempPublicKey = new BCRSAPublicKey(rsaKeyParams);
    } else {
      byte[] tempPublicKeyX = new byte[32];
      byte[] tempPublicKeyY = new byte[32];
      System.arraycopy(data, 8, tempPublicKeyX, 0, 32);
      System.arraycopy(data, 72, tempPublicKeyY, 0, 32);

      this.tempPublicKey = new SM2PublicKey(tempPublicKeyX, tempPublicKeyY);
    }
  }

  public PublicKey getTemporaryPublicKey()
  {
    return this.tempPublicKey;
  }

  public byte[] getTemporaryPublicKeyDataFromAttributes(ASN1Set attributes)
    throws PKIException
  {
    byte[] tmpPublicKey = null;

    if ((attributes != null) && (attributes.size() > 1)) {
      ASN1Sequence sequence = (ASN1Sequence)attributes.getObjectAt(1);
      ASN1ObjectIdentifier oid = ASN1ObjectIdentifier.getInstance(sequence.getObjectAt(0));
      if (!oid.equals(PKCSObjectIdentifiers.pkcs_9_at_tempPublicKey)) {
        throw new PKIException(PKIException.PARSE_P10_ERR, PKIException.PARSE_P10_ERR_ATTRI_ID, new Exception("Not support the Attributes[1] Type ID:" + oid.getId()));
      }

      ASN1OctetString asn1oct = ASN1OctetString.getInstance(sequence.getObjectAt(1));
      sequence = ASN1Parser.parseOCT2SEQ(asn1oct);
      byte[] data = ((ASN1OctetString)sequence.getObjectAt(1)).getOctets();
      if (!"sm3WithSM2Encryption".equals(this.signAlg)) {
        RSAPublicKey rsaPubKey = RSAPublicKey.getInstance(data);
        try {
          tmpPublicKey = Base64.encode(rsaPubKey.getEncoded());
        } catch (IOException e) {
          e.printStackTrace();
        }
      } else {
        tmpPublicKey = Base64.encode(data);
      }
    }
    return tmpPublicKey;
  }

  /** @deprecated */
  public String getTemporaryPublicKeyFromAttributes(ASN1Set attributes)
    throws PKIException
  {
    try
    {
      return new String(getTemporaryPublicKeyDataFromAttributes(attributes), "UTF-8");
    } catch (UnsupportedEncodingException e) {
      throw new PKIException("codeing Exception", e);
    }
  }

  /** @deprecated */
  public String generateCertificationRequest(Mechanism mechanism, X500Name x500NameSubject, ASN1Set attributes, PublicKey publicKey, PrivateKey privateKey)
    throws PKIException
  {
    try
    {
      return new String(generatePKCS10Request(mechanism, x500NameSubject, attributes, publicKey, privateKey), "UTF-8");
    } catch (UnsupportedEncodingException e) {
      throw new PKIException("codeing Exception", e);
    }
  }

  public byte[] generateDoublePKCS10Request(Mechanism mechanism, int keyLength, Session session)
    throws PKIException
  {
    this.session = session;
    return generateDoublePKCS10Request(mechanism, keyLength);
  }

  public byte[] generateDoublePKCS10Request(Mechanism mechanism, int keyLength)
    throws PKIException
  {
    String signAlgValue = mechanism.getMechanismType();
    Mechanism enMechanism = null;
    if (("md5WithRSAEncryption".equals(signAlgValue)) || ("sha1WithRSAEncryption".equals(signAlgValue)) || ("sha256WithRSAEncryption".equals(signAlgValue)) || ("sha512WithRSAEncryption".equals(signAlgValue)))
    {
      enMechanism = new Mechanism("RSA");
    } else if ("sm3WithSM2Encryption".equals(signAlgValue)) {
      enMechanism = new Mechanism("SM2");
      enMechanism.setParam(mechanism.getParam());
    }

    if (null == enMechanism) {
      throw new PKIException("unsupported algorithm: " + mechanism.getMechanismType());
    }

    this.keyPair = KeyUtil.generateKeyPair(enMechanism, keyLength, this.session);
    this.tempKeyPair = KeyUtil.generateKeyPair(enMechanism, keyLength, this.session);
    SubjectPublicKeyInfo publicKeyInfo = SubjectPublicKeyInfo.getInstance(this.keyPair.getPublic().getEncoded());
    SubjectPublicKeyInfo temporaryPublicKeyInfo = SubjectPublicKeyInfo.getInstance(this.tempKeyPair.getPublic().getEncoded());
    X500Name subject = new X500Name("CN=certRequisition,O=CFCA TEST CA,C=CN");

    ASN1EncodableVector challengePasswordVector = new ASN1EncodableVector();
    DERPrintableString challengePasswordValue = new DERPrintableString("111111");
    challengePasswordVector.add(PKCSObjectIdentifiers.pkcs_9_at_challengePassword);
    challengePasswordVector.add(challengePasswordValue);
    DERSequence challengePasswordSeq = new DERSequence(challengePasswordVector);

    ASN1EncodableVector tempPublicKeyVector = new ASN1EncodableVector();
    tempPublicKeyVector.add(PKCSObjectIdentifiers.pkcs_9_at_tempPublicKey);
    ASN1Integer derInt = new ASN1Integer(1L);
    DEROctetString tempOctetString = null;
    if ("RSA".equals(enMechanism.getMechanismType())) {
      try {
        tempOctetString = new DEROctetString(temporaryPublicKeyInfo.parsePublicKey());
      } catch (IOException e1) {
        e1.printStackTrace();
      }
    } else if ("SM2".equals(enMechanism.getMechanismType())) {
      byte[] tempPublicKeyData = new byte[''];
      byte[] temporaryPublicKeyData = temporaryPublicKeyInfo.getPublicKeyData().getBytes();
      byte[] temporaryPublicKeyX = new byte[32];
      byte[] temporaryPublicKeyY = new byte[32];
      System.arraycopy(temporaryPublicKeyData, 1, temporaryPublicKeyX, 0, 32);
      System.arraycopy(temporaryPublicKeyData, 33, temporaryPublicKeyY, 0, 32);

      byte[] CONST1 = { 0, -76, 0, 0 };
      byte[] CONST2 = { 0, 1, 0, 0 };
      System.arraycopy(CONST1, 0, tempPublicKeyData, 0, 4);
      System.arraycopy(CONST2, 0, tempPublicKeyData, 4, 4);
      System.arraycopy(temporaryPublicKeyX, 0, tempPublicKeyData, 8, 32);
      System.arraycopy(temporaryPublicKeyY, 0, tempPublicKeyData, 72, 32);
      tempOctetString = new DEROctetString(tempPublicKeyData);
    }

    ASN1EncodableVector intAndPublicKeyVector = new ASN1EncodableVector();
    intAndPublicKeyVector.add(derInt);
    intAndPublicKeyVector.add(tempOctetString);
    DERSequence tempPublicKeyInfoSeq = new DERSequence(intAndPublicKeyVector);
    DEROctetString intAndPublicKeyOctetString = null;
    try {
      intAndPublicKeyOctetString = new DEROctetString(tempPublicKeyInfoSeq);
    }
    catch (IOException e1) {
      e1.printStackTrace();
    }
    tempPublicKeyVector.add(intAndPublicKeyOctetString);
    DERSequence tempPubKeySeq = new DERSequence(tempPublicKeyVector);
    ASN1EncodableVector chaPasswordAndPubKeyVector = new ASN1EncodableVector();
    chaPasswordAndPubKeyVector.add(challengePasswordSeq);
    chaPasswordAndPubKeyVector.add(tempPubKeySeq);
    this.attributes = new DERSet(chaPasswordAndPubKeyVector);

    CertificationRequestInfo certRequestInfo = new CertificationRequestInfo(subject, publicKeyInfo, this.attributes);
    Object obj = Mechanism.ALGOIDMap.get(signAlgValue);
    AlgorithmIdentifier signAlg = new AlgorithmIdentifier(ASN1ObjectIdentifier.getInstance(obj), DERNull.INSTANCE);
    try
    {
      byte[] sourceData = certRequestInfo.getEncoded("DER");
      byte[] signature = this.session.sign(mechanism, this.keyPair.getPrivate(), sourceData);

      if ("sm3WithSM2Encryption".equals(signAlgValue)) {
        signature = prepareSignature(signature, true);
      }

      CertificationRequest certRequest = new CertificationRequest(certRequestInfo, signAlg, new DERBitString(signature));
      PKCS10CertificationRequest p10 = new PKCS10CertificationRequest(certRequest);
      return Base64.encode(p10.getEncoded());
    } catch (Exception e) {
      throw new PKIException(e.getMessage());
    }
  }

  private byte[] prepareSignature(byte[] signature, boolean isASN1Encoding)
    throws IOException
  {
    if (isASN1Encoding) {
      byte[] tmpSignature = (byte[])signature.clone();
      byte firstBytePubX = tmpSignature[0];
      byte firstBytePubY = tmpSignature[32];
      byte[] pubXBytes = null;
      byte[] pubYBytes = null;
      if ((firstBytePubX & 0x80) == 0) {
        pubXBytes = new byte[32];
        System.arraycopy(tmpSignature, 0, pubXBytes, 0, 32);
      } else {
        pubXBytes = new byte[33];
        pubXBytes[0] = 0;
        System.arraycopy(tmpSignature, 0, pubXBytes, 1, 32);
      }

      if ((firstBytePubY & 0x80) == 0) {
        pubYBytes = new byte[32];
        System.arraycopy(tmpSignature, 32, pubYBytes, 0, 32);
      } else {
        pubYBytes = new byte[33];
        pubYBytes[0] = 0;
        System.arraycopy(tmpSignature, 32, pubYBytes, 1, 32);
      }

      ASN1Integer pubXInt = new ASN1Integer(pubXBytes);
      ASN1Integer pubYInt = new ASN1Integer(pubYBytes);

      ASN1EncodableVector pubXYVector = new ASN1EncodableVector();
      pubXYVector.add(pubXInt);
      pubXYVector.add(pubYInt);
      DERSequence pubXYSeq = new DERSequence(pubXYVector);
      byte[] asn1Signature = pubXYSeq.getEncoded();
      return asn1Signature;
    }
    byte[] tmpASN1Signature = (byte[])signature.clone();
    ASN1Sequence asn1Seq = ASN1Sequence.getInstance(tmpASN1Signature);
    ASN1Integer pubXInt = (ASN1Integer)asn1Seq.getObjectAt(0);
    ASN1Integer pubYInt = (ASN1Integer)asn1Seq.getObjectAt(1);
    byte[] tmpPubXBytes = pubXInt.getValue().toByteArray();
    byte[] tmpPubYBytes = pubYInt.getValue().toByteArray();
    byte[] pubXBytes = new byte[32];
    byte[] pubYBytes = new byte[32];
    byte[] rawSignature = new byte[64];
    byte firstByte = tmpPubXBytes[0];
    if (firstByte == 0)
      System.arraycopy(tmpPubXBytes, 1, pubXBytes, 0, 32);
    else {
      System.arraycopy(tmpPubXBytes, 0, pubXBytes, 0, 32);
    }
    firstByte = tmpPubYBytes[0];
    if (firstByte == 0)
      System.arraycopy(tmpPubYBytes, 1, pubYBytes, 0, 32);
    else {
      System.arraycopy(tmpPubYBytes, 0, pubYBytes, 0, 32);
    }
    System.arraycopy(pubXBytes, 0, rawSignature, 0, 32);
    System.arraycopy(pubYBytes, 0, rawSignature, 32, 32);
    return rawSignature;
  }

  public byte[] generatePKCS10Request(Mechanism mechanism, int keyLength, Session session)
    throws PKIException
  {
    this.session = session;
    return generatePKCS10Request(mechanism, keyLength);
  }

  public byte[] generatePKCS10Request(Mechanism mechanism, int keyLength)
    throws PKIException
  {
    String signAlgValue = mechanism.getMechanismType();
    Mechanism enMechanism = null;
    if (("md5WithRSAEncryption".equals(signAlgValue)) || ("sha1WithRSAEncryption".equals(signAlgValue)) || ("sha256WithRSAEncryption".equals(signAlgValue)) || ("sha512WithRSAEncryption".equals(signAlgValue)))
    {
      enMechanism = new Mechanism("RSA");
    } else if ("sm3WithSM2Encryption".equals(signAlgValue)) {
      enMechanism = new Mechanism("SM2");
      enMechanism.setParam(mechanism.getParam());
    }

    if (null == enMechanism) {
      throw new PKIException("unsupported algorithm: " + mechanism.getMechanismType());
    }

    this.keyPair = KeyUtil.generateKeyPair(enMechanism, keyLength, this.session);
    SubjectPublicKeyInfo pubInfo = SubjectPublicKeyInfo.getInstance(this.keyPair.getPublic().getEncoded());
    X500Name subject = new X500Name("CN=certRequisition,O=CFCA TEST CA,C=CN");
    CertificationRequestInfo certRequestInfo = new CertificationRequestInfo(subject, pubInfo, null);
    Object obj = Mechanism.ALGOIDMap.get(signAlgValue);
    AlgorithmIdentifier signAlgID = new AlgorithmIdentifier(ASN1ObjectIdentifier.getInstance(obj), DERNull.INSTANCE);
    try {
      byte[] sourceData = certRequestInfo.getEncoded("DER");
      byte[] signature = this.session.sign(mechanism, this.keyPair.getPrivate(), sourceData);
      if ("sm3WithSM2Encryption".equals(signAlgValue)) {
        signature = prepareSignature(signature, true);
      }

      CertificationRequest certRequest = new CertificationRequest(certRequestInfo, signAlgID, new DERBitString(signature));
      PKCS10CertificationRequest p10 = new PKCS10CertificationRequest(certRequest);
      return Base64.encode(p10.getEncoded());
    } catch (Exception e) {
      throw new PKIException(e.getMessage());
    }
  }

  public byte[] generatePKCS10Request(Mechanism mechanism, X500Name x500NameSubject, ASN1Set attributes, PublicKey publicKey, PrivateKey privateKey, Session session)
    throws PKIException
  {
    this.session = session;
    return generatePKCS10Request(mechanism, x500NameSubject, attributes, publicKey, privateKey);
  }

  public byte[] generatePKCS10Request(Mechanism mechanism, X500Name x500NameSubject, ASN1Set attributes, PublicKey publicKey, PrivateKey privateKey)
    throws PKIException
  {
    SubjectPublicKeyInfo pubInfo = SubjectPublicKeyInfo.getInstance(publicKey.getEncoded());
    CertificationRequestInfo certRequestInfo = new CertificationRequestInfo(x500NameSubject, pubInfo, attributes);
    String signAlgString = mechanism.getMechanismType();
    Object obj = Mechanism.ALGOIDMap.get(signAlgString);
    AlgorithmIdentifier signAlgID = new AlgorithmIdentifier(ASN1ObjectIdentifier.getInstance(obj), DERNull.INSTANCE);
    try {
      byte[] sourceData = certRequestInfo.getEncoded("DER");
      byte[] signature = this.session.sign(mechanism, privateKey, sourceData);
      if ("sm3WithSM2Encryption".equals(signAlgString)) {
        signature = prepareSignature(signature, true);
      }
      CertificationRequest certRequest = new CertificationRequest(certRequestInfo, signAlgID, new DERBitString(signature));
      PKCS10CertificationRequest p10 = new PKCS10CertificationRequest(certRequest);
      return Base64.encode(p10.getEncoded());
    } catch (Exception e) {
      throw new PKIException(e.getMessage());
    }
  }

  public byte[] generatePKCS10Request(Mechanism mechanism, X500Name x500NameSubject, ASN1Set attributes, PublicKey publicKey, byte[] signature)
    throws PKIException
  {
    SubjectPublicKeyInfo pubInfo = SubjectPublicKeyInfo.getInstance(publicKey.getEncoded());
    CertificationRequestInfo certRequestInfo = new CertificationRequestInfo(x500NameSubject, pubInfo, attributes);
    Object obj = Mechanism.ALGOIDMap.get(mechanism.getMechanismType());
    AlgorithmIdentifier signAlgID = new AlgorithmIdentifier(ASN1ObjectIdentifier.getInstance(obj), DERNull.INSTANCE);
    try {
      CertificationRequest certRequest = new CertificationRequest(certRequestInfo, signAlgID, new DERBitString(signature));
      PKCS10CertificationRequest p10 = new PKCS10CertificationRequest(certRequest);
      return Base64.encode(p10.getEncoded());
    } catch (Exception e) {
      throw new PKIException(e.getMessage());
    }
  }

  public byte[] generatePKCS10Request(Mechanism mechanism, CertificationRequestInfo certRequestInfo, byte[] signature)
    throws PKIException
  {
    Object obj = Mechanism.ALGOIDMap.get(mechanism.getMechanismType());
    AlgorithmIdentifier signAlgID = new AlgorithmIdentifier(ASN1ObjectIdentifier.getInstance(obj), DERNull.INSTANCE);
    try {
      CertificationRequest certRequest = new CertificationRequest(certRequestInfo, signAlgID, new DERBitString(signature));
      PKCS10CertificationRequest p10 = new PKCS10CertificationRequest(certRequest);
      return Base64.encode(p10.getEncoded());
    } catch (Exception e) {
      throw new PKIException(e.getMessage());
    }
  }

  public CertificationRequestInfo generateCertificationRequestInfo(X500Name x500NameSubject, ASN1Set attributes, PublicKey publicKey)
  {
    SubjectPublicKeyInfo pubInfo = SubjectPublicKeyInfo.getInstance(publicKey.getEncoded());
    CertificationRequestInfo certRequestInfo = new CertificationRequestInfo(x500NameSubject, pubInfo, attributes);
    return certRequestInfo;
  }

  public byte[] parseCertificationRequestInfoToBytes(CertificationRequestInfo certRequestInfo)
    throws PKIException
  {
    byte[] sourceData = null;
    try {
      sourceData = certRequestInfo.getEncoded("DER");
    } catch (IOException e) {
      throw new PKIException(e.getMessage());
    }
    return sourceData;
  }

  public KeyPair getKeyPair()
  {
    return this.keyPair;
  }

  public KeyPair getTemporaryKeyPair()
  {
    return this.tempKeyPair;
  }

  public PrivateKey getTemporaryPrivateKey()
  {
    return this.tempKeyPair.getPrivate();
  }

  public PrivateKey getPrivateKey()
  {
    return this.keyPair.getPrivate();
  }

  public String getSubjectFromP10Request(byte[] p10Request)
    throws PKIException
  {
    try
    {
      PKCS10CertificationRequest p10 = new PKCS10CertificationRequest(Base64.decode(p10Request));
      return p10.getSubject().toString();
    } catch (Exception e) {
      throw new PKIException(e.getMessage());
    }
  }

  public String getSignatureAlgorithmFromP10Request(byte[] p10Request)
    throws PKIException
  {
    try
    {
      PKCS10CertificationRequest p10 = new PKCS10CertificationRequest(Base64.decode(p10Request));
      ASN1ObjectIdentifier oid = p10.getSignatureAlgorithm().getAlgorithm();
      boolean isContain = Mechanism.OIDALGMap.containsKey(oid);
      if (!isContain) {
        return oid.getId();
      }
      return (String)Mechanism.OIDALGMap.get(oid);
    }
    catch (Exception e)
    {
      throw new PKIException(e.getMessage());
    }
  }

  public String getSignatureAlgorithm() {
    return this.signAlg;
  }

  public byte[] getSignatureFromP10Request(byte[] p10Request)
    throws PKIException
  {
    try
    {
      PKCS10CertificationRequest p10 = new PKCS10CertificationRequest(Base64.decode(p10Request));
      return p10.getSignature();
    } catch (Exception e) {
      throw new PKIException(e.getMessage());
    }
  }

  public byte[] getSignature() {
    return this.signature;
  }

  public boolean isP10RequestSignatureValid(byte[] p10Request)
    throws PKIException
  {
    try
    {
      PKCS10CertificationRequest p10 = new PKCS10CertificationRequest(Base64.decode(p10Request));
      byte[] sign = p10.getSignature();
      byte[] src = p10.toASN1Structure().getCertificationRequestInfo().getEncoded("DER");
      ASN1ObjectIdentifier oid = p10.getSignatureAlgorithm().getAlgorithm();
      boolean isContain = Mechanism.OIDALGMap.containsKey(oid);
      if (!isContain) {
        throw new PKIException("can not support such sign Alg:" + oid.toString());
      }
      String sigAlgName = (String)Mechanism.OIDALGMap.get(oid);
      return new Signature().p1VerifyMessage(sigAlgName, src, sign, getPubKeyFromSubPubKeyInfo(p10.getSubjectPublicKeyInfo()), this.session);
    }
    catch (Exception e)
    {
      throw new PKIException(e.getMessage());
    }
  }

  /** @deprecated */
  public boolean getP10RequestVerifyState()
  {
    return this.p10RequestVerifyState;
  }

  public PublicKey getPubKeyFromSubPubKeyInfo(SubjectPublicKeyInfo spki)
    throws PKIException
  {
    try
    {
      if (spki == null) {
        return null;
      }
      if ((SM2OIDUtil.isSm3WithSM2Encryption(spki.getAlgorithm().getAlgorithm())) || (SM2OIDUtil.isSM2PublicKeyOID(spki.getAlgorithm().getParameters()))) {
        byte[] pubData = spki.getPublicKeyData().getBytes();
        int len = pubData.length;
        if (len == 65) {
          byte[] pubX = new byte[32];
          byte[] pubY = new byte[32];
          System.arraycopy(pubData, 1, pubX, 0, 32);
          System.arraycopy(pubData, 33, pubY, 0, 32);
          return KeyUtil.getSM2PublicKey(pubX, pubY);
        }
        throw new PKIException(PKIException.SPKI_KEY, PKIException.SPKI_KEY_DES);
      }

      if (spki.getAlgorithm().getAlgorithm().toString().equals(PKCSObjectIdentifiers.rsaEncryption.toString())) {
        RSAKeyParameters param = (RSAKeyParameters)PublicKeyFactory.createKey(spki);
        return new BCRSAPublicKey(param);
      }
      throw new PKIException("can not support this key type:" + spki.getAlgorithm().getAlgorithm().toString());
    }
    catch (Exception e)
    {
      throw new PKIException(e.getMessage());
    }
  }

  public int getCertReqType()
  {
    return this.certReqType;
  }
}